﻿//=============================================================================
// SharpTemplate.NET - Generic template parser
//
// Copyright (c) 2008 Philippe Leybaert
//
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to deal 
// in the Software without restriction, including without limitation the rights 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//=============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Activa.LazyParser;

namespace Activa.SharpTemplate
{
    public enum TemplateTokenType
    {
        Expression,
        Statement,
        ForEach,
        EndBlock,
        If,
        Else,
        ElseIf,
        Include,
        Render,
        MacroBegin,
        MacroEnd
    }

    public abstract class SharpTemplateConfig
    {
        private class TokenDefinition
        {
            public TokenDefinition(string regex, TemplateTokenType tokenType, bool removeEmptyLine, string tokenId)
            {
                Regex = regex;
                TokenType = tokenType;
                RemoveEmptyLine = removeEmptyLine;
                TokenId = tokenId;
            }

            public readonly string Regex;
            public readonly TemplateTokenType TokenType;
            public readonly bool RemoveEmptyLine;
            public readonly string TokenId;
        }

        private readonly List<TokenDefinition> _tokenDefinitions = new List<TokenDefinition>();
        private Regex _regex;

        public void AddToken(TemplateTokenType tokenType, string regex)
        {
            AddToken(tokenType, regex, false, null);
        }

        public void AddToken(TemplateTokenType tokenType, string regex , string tokenId)
        {
            AddToken(tokenType, regex, false, tokenId);
        }

        public void AddToken(TemplateTokenType tokenType, string regex, bool stripEmptyLine)
        {
            AddToken(tokenType, regex, stripEmptyLine, null);
            
        }
        public void AddToken(TemplateTokenType tokenType, string regex, bool stripEmptyLine, string tokenId)
        {
            _tokenDefinitions.Add(new TokenDefinition(regex, tokenType, stripEmptyLine, tokenId));
        }

        internal TokenMatch FindTokenMatch(Regex regex, Match m)
        {
            for (int i = 0; i < _tokenDefinitions.Count; i++)
                if (m.Groups[i + 100].Success)
                    return new TokenMatch(regex, m, _tokenDefinitions[i].TokenType, _tokenDefinitions[i].RemoveEmptyLine , _tokenDefinitions[i].TokenId);

            return null;
        }

        internal Regex Regex
        {
            get
            {
                if (_regex != null)
                    return _regex;

                string s = "";

                for (int i = 0; i < _tokenDefinitions.Count; i++)
                {
                    if (string.IsNullOrEmpty(_tokenDefinitions[i].Regex))
                        continue;

                    if (s.Length > 0)
                        s += "|";

                    s += "(?<" + (i + 100) + ">" + _tokenDefinitions[i].Regex + ")";
                }

                return new Regex(s, RegexOptions.Singleline);
            }
        }

        protected virtual bool OnEvalIf(ExpressionParser parser, TokenMatch tokenMatch, IParserContext context)
        {
            return context.ToBoolean(parser.Evaluate(tokenMatch.SubMatches["expr"], context).Value);
        }

        protected virtual string OnEvalExpression(ExpressionParser parser, TokenMatch tokenMatch, IParserContext context)
        {
            object value = parser.Evaluate(tokenMatch.SubMatches["expr"], context).Value;

            if (value == null)
                return null;

            if (tokenMatch.SubMatches.ContainsKey("fmt"))
                return string.Format("{0:" + tokenMatch.SubMatches["fmt"] + "}", value);

            if (value is string)
                return (string) value;

            return value.ToString();
        }

        protected virtual IEnumerable OnEvalForeach(ExpressionParser parser, TokenMatch tokenMatch, IParserContext context)
        {
            return parser.Evaluate(tokenMatch.SubMatches["expr"], context).Value as IEnumerable;
        }

        protected virtual string OnEvalText(string text)
        {
            return text;
        }

        protected virtual void OnEvalIteration(string iteratorName, int rowNum, object obj, IParserContext localContext)
        {
            localContext.SetLocal(iteratorName + "@row", rowNum);
            localContext.SetLocal(iteratorName + "@odd", rowNum % 2 == 1);
            localContext.SetLocal(iteratorName + "@even", rowNum % 2 == 0);
            localContext.SetLocal(iteratorName + "@oddeven", rowNum % 2 == 0 ? "even" : "odd");
            localContext.SetLocal(iteratorName + "@ODDEVEN", rowNum % 2 == 0 ? "EVEN" : "ODD");
            localContext.SetLocal(iteratorName + "@OddEven", rowNum % 2 == 0 ? "Even" : "Odd");
        }

        internal IEnumerable EvalForeach(ExpressionParser parser, TokenMatch tokenMatch, IParserContext context)
        {
            return OnEvalForeach(parser, tokenMatch, context);
        }

        internal bool EvalIf(ExpressionParser parser, TokenMatch tokenMatch, IParserContext context)
        {
            return OnEvalIf(parser, tokenMatch, context);
        }

        internal string EvalExpression(ExpressionParser parser, TokenMatch tokenMatch, IParserContext context)
        {
            return OnEvalExpression(parser, tokenMatch, context);
        }

        internal string EvalText(string text)
        {
            return OnEvalText(text);
        }

        internal void EvalIteration(string iteratorName, int rowNum, object obj, IParserContext localContext)
        {
            OnEvalIteration(iteratorName, rowNum, obj, localContext);
        }


    }
}